package xyz.thetbw.autocode

import xyz.thetbw.autocode.utils.VelocityUtils
import com.sksamuel.hoplite.ConfigLoader
import com.sksamuel.hoplite.PropertySource
import mu.KotlinLogging
import org.apache.velocity.VelocityContext
import org.apache.velocity.context.Context
import xyz.thetbw.autocode.utils.StringCaseUtils
import java.io.File
import kotlin.system.exitProcess
import kotlin.text.StringBuilder

private val logger = KotlinLogging.logger {  }

//模板描述
class TemplateDescription(var model: DataModel) {
    val context: Context

    lateinit var children: List<TemplateChild>

    init {
        //初始化模板上下文
        context = VelocityContext()
        context.put("dataFields",model.dataFields)
        context.put("database",model.database)
        context.put("table",model.table)
        context.put("tableComment",model.tableComment)
        context.put("StringUtils",StringCaseUtils)
    }

    //加载模板信息
    fun load(inputDir: String){
        val scanDir  = File(inputDir)
        if (!scanDir.exists() && scanDir.list()?.size == 0){
            logger.error { "扫描文件夹为空" }
        }
        //模板初始化文件 init
        val initFile = scanDir.listFiles{ _: File?, name: String? -> name != null && name == Constants.TEMPLATE_INIT_FILE_NAME+Config.instance.templateExt }
        if (initFile != null && initFile.size == 1){
            logger.info { "找到 ${initFile[0].name},开始渲染模板初始化文件" }
            VelocityUtils.render(initFile[0],context)
        }

        this.children =  loadChildren(inputDir,null)
        children.forEach { it.loadChildren() }
    }

    //渲染模板信息
    fun render(){
        children.forEach { it.render() }
    }

    fun loadChildren(path: String, parent: TemplateChild?): List<TemplateChild> {
        val loadOptions = getLoadOptions(path)
        val thisDir = File(path)
        val subFiles = thisDir.listFiles()
        val templateChildren = ArrayList<TemplateChild>()
        subFiles?.forEach {
            //如果存在模板初始化文件的话跳过，因为在加载时已经优先处理了
            if (parent == null && it.name.equals(Constants.TEMPLATE_INIT_FILE_NAME+Config.instance.templateExt)){
                return@forEach
            }
            if (it.name == Constants.LOAD_OPTIONS_FILE_NAME) return@forEach
            val child: TemplateChild = if (it.isDirectory )  TemplateDirChild(parent) else TemplateFileChild(parent)
            templateChildren.add(
                child.apply {
                    originName = it.name
                    renderName = VelocityUtils.render(originName,context)
                }
            )
        }
        if (loadOptions != null){
            templateChildren.removeIf{
                loadOptions.excludeFields.contains(it.renderName)
            }
        }
        return templateChildren
    }

    private fun getLoadOptions(path: String): LoadOptions?{
        val thisDir = File(path)
        val loadOptionFile = File(thisDir, Constants.LOAD_OPTIONS_FILE_NAME)
        if (!loadOptionFile.exists()){
            return null
        }
        val optionsStr = VelocityUtils.render(loadOptionFile,context)
        try {
             return ConfigLoader.Builder().addSource(PropertySource.string(optionsStr,"yaml"))
                 .build().loadConfigOrThrow()
        }catch (e: Exception){
            logger.error { path }
            logger.error { "当前文件下的 ${Constants.LOAD_OPTIONS_FILE_NAME} 格式错误: ${e.message}" }
            exitProcess(1)
        }
    }

    //模板项
    abstract inner class TemplateChild(private val parent: TemplateChild?){
        lateinit var originName: String  //原始文件名
        var renderName: String = ""//渲染的文件名
            get()  {
                if (Config.instance.templateExt == null){
                    return field
                }
                if (field.endsWith(Config.instance.templateExt!!)){
                    return field.substring(0,field.indexOf(Config.instance.templateExt!!))
                }
                return field
            }
        lateinit var children: List<TemplateChild>

        //获取原始文件路径
        fun getOriginPath(): String{
            val builder  = getPath { originName }
            builder.insert(0, Config.instance.loadDir+File.separator)
            return builder.toString()
        }

        //获取渲染文件路径
        fun getRenderPath(): String{
            val builder  = getPath { renderName }
            builder.insert(0, Config.instance.outDir+File.separator)
            return builder.toString()

        }

        private fun getPath(nameProvide: TemplateChild.()->String): StringBuilder{
            val stringBuilder =  StringBuilder()
            stringBuilder.append(nameProvide())
            var child = this
            while (child.parent != null){
                stringBuilder.insert(0,child.parent!!.nameProvide() + File.separator)
                child = child.parent!!
            }
            return stringBuilder
        }


        open fun loadChildren(){children = ArrayList()}

        abstract fun render() //开始渲染
    }

    //模板文件
    inner class TemplateFileChild(parent: TemplateChild?): TemplateChild(parent){

        override fun render() {
            val outFile = File(getRenderPath())
            val inField = File(getOriginPath())
            VelocityUtils.renderToFile(inField,context,outFile)
        }

    }

    //模板目录
    inner class TemplateDirChild(parent: TemplateChild?): TemplateChild(parent){

        override  fun loadChildren() {
            val templateChildren = this@TemplateDescription.loadChildren(getOriginPath(),this)
            this.children  = templateChildren
            this.children.forEach{ it.loadChildren()}
        }

        override fun render() {
            val file = File(getRenderPath())
            if (!file.exists()){
                file.mkdirs()
            }
            children.forEach { it.render() }
        }
    }


    data class LoadOptions(
        val excludeFields : List<String> //排除的文件名
    )

}